using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.IO;
using System.Collections.Specialized;
using GPSCore.NMEA.Interfaces;

namespace GPSCore.NMEA.COMSource
{
    #region COMPort

    /// <summary>
    /// COM Port in order to obtain GPS Values
    /// </summary>
    public class COMPort : IDisposable, INMEASource
    {
        #region members

        private System.IO.Ports.SerialPort m_serialPort;

        //private bool m_doRead;
        //private ThreadStart m_readThreadStart; 
        //private Thread m_readThread; 

        private string m_portName;
        private int m_baudRate;
        private System.IO.Ports.Parity m_parity;
        private int m_dataBits;
        private System.IO.Ports.StopBits m_stopBits;

        #endregion

        #region constructors

        /// <summary>
        /// Void constructor
        /// </summary>
        public COMPort()
        {
            m_serialPort = null;
        }

        /// <summary>
        /// Build a COMPort object.
        /// 
        /// The portName must be in this format: "COMxxx" where xxx is the number of the port
        /// </summary>
        public COMPort(string portName, int baudRate, System.IO.Ports.Parity parity, int dataBits, System.IO.Ports.StopBits stopBits )
        {
            m_portName = portName;
            m_baudRate = baudRate;
            m_parity = parity;
            m_dataBits = dataBits;
            m_stopBits = stopBits;

            m_serialPort = null;
        }

        #endregion

        #region events

        /// <summary>
        /// Event Raised when a line is received.
        /// 
        /// GPS data is divided in lines
        /// </summary>
        public event NMEASentenceRecieved OnNMEASentenceRecieved;

        /// <summary>
        /// On ComPort Open
        /// </summary>
        public event ComPortOpen OnComPortOpen;

        /// <summary>
        /// On ComPort Closed
        /// </summary>
        public event ComPortClosed OnComPortClosed;

        /// <summary>
        /// On ComPort Error
        /// </summary>
        public event ComPortError OnComPortError;

        #endregion

        #region properties

        /// <summary>
        /// Get the list of available COM Ports
        /// </summary>
        public static string[] GetPortNames
        { 
            get
            {
                return System.IO.Ports.SerialPort.GetPortNames();
            }        
        }

        /// <summary>
        /// COM Port is open?
        /// </summary>
        public bool IsComportOpen
        {
            get 
            {
                try
                {
                    return m_serialPort.IsOpen;
                }
                catch 
                {
                    return false;
                }
            }        
        }

        #endregion

        #region membres

        /// <summary>
        /// Open COM Port and start to receive GPS Data
        /// </summary>
        public bool Start()
        {
            try
            {
                if (m_serialPort == null)
                {
                    //create a new istance of the port
                    m_serialPort = new System.IO.Ports.SerialPort(m_portName, m_baudRate, m_parity, m_dataBits, m_stopBits);

                    m_serialPort.ReadBufferSize = 1024; //'2K
                    m_serialPort.ReadTimeout = 1000;
                    m_serialPort.WriteTimeout = 1000;
                }

                //set the receive event handler
                m_serialPort.DataReceived += new System.IO.Ports.SerialDataReceivedEventHandler(COMDataReceived);
                
                //open the serial port
                m_serialPort.Open();

                if (OnComPortOpen != null)
                    OnComPortOpen.Invoke();

                //m_doRead = true;

                //initThread();

                //start the reading thred
                //m_readThread.IsBackground = true;
                //m_readThread.Start();

                return true;
            }
            catch (Exception ex)
            {
                if (OnComPortError != null)
                    OnComPortError.Invoke(ex, "Error calling Open() method");
                
                return false;
            }      
        }

        /// <summary>
        /// Close COM Port
        /// </summary>
        /// <returns></returns>
        public bool Stop()
        {
            try
            {
                if (m_serialPort != null)
                {
                    if (m_serialPort.IsOpen)
                    {
                        //m_doRead = false;

                        //if (m_serialPort != null)
                        //{
                            m_serialPort.Close();          
                        //}                        
                    }

                    m_serialPort.Dispose();
                    m_serialPort = null;

                    if (OnComPortClosed != null)
                        OnComPortClosed.Invoke();
                }               

                return true;
            }
            catch (Exception ex)
            {
                if (OnComPortError != null)
                    OnComPortError.Invoke(ex, "Error Closing the COM Port");

                return false;
            }

            return false;
        }

        #region thread - no more used

        /*
         
        /// <summary>
        /// Initialize the Thread
        /// </summary>
        private void initThread()
        {
            m_readThreadStart = new ThreadStart(readingThread);
            m_readThread = new Thread(m_readThreadStart);
        }

        /// <summary>
        /// internal reading thread
        /// </summary>
        private void readingThread()
        {
            string message;

            while (m_doRead)
            {
                try
                {
                    if (m_serialPort == null)
                        return;

                    message = m_serialPort.ReadLine();

                    //FIRE EXTERNALLY AN EVENT!
                    if (OnLineRecieved != null)
                    {
                        OnLineRecieved.Invoke(message);
                    }
                }
                catch (System.IO.IOException ioEx)
                {
                    if (OnComPortError != null)
                        OnComPortError.Invoke(ioEx, "IO Exception happens");
                }
                catch (TimeoutException timeOutEx)
                {
                    if (OnComPortError != null)
                        OnComPortError.Invoke(timeOutEx, "IO Exception happens");                  
                }


            }

            m_serialPort.Close();
            m_serialPort.Dispose();
            m_serialPort = null;

            if (OnComPortClosed != null)
                OnComPortClosed.Invoke();        
        }

        */

        #endregion

        /// <summary>
        /// Internal method called when data is received from the serial port....
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="Args"></param>
        private void COMDataReceived( object sender , System.IO.Ports.SerialDataReceivedEventArgs e)
        {
            string message = String.Empty;

            if (e.EventType == System.IO.Ports.SerialData.Chars)
            {
                try
                {
                    message = ((System.IO.Ports.SerialPort)sender).ReadLine();

                    //FIRE EXTERNALLY AN EVENT!
                    if (OnNMEASentenceRecieved != null)
                    {
                        NMEASentenceRecieved extEvent = new NMEASentenceRecieved(OnNMEASentenceRecieved);

                        extEvent.Invoke(message);
                    }
                }
                catch (System.IO.IOException ioEx)
                {
                    if (OnComPortError != null)
                        OnComPortError.Invoke(ioEx, "IO Exception happens");
                }
                catch (TimeoutException timeOutEx)
                {
                    if (OnComPortError != null)
                        OnComPortError.Invoke(timeOutEx, "IO Exception happens");
                }
            }
        }

        #endregion

        #region IDisposable Members

        /// <summary>
        /// Dispose correctly the Object
        /// </summary>
        void IDisposable.Dispose()
        {
            if (m_serialPort != null)
            {
                //if (m_doRead)
                //{
                //    m_doRead = false;
                //}

                if (m_serialPort != null)
                {
                    if ( m_serialPort.IsOpen )
                        m_serialPort.Close();

                    m_serialPort.Dispose();
                    m_serialPort = null;
                }
            }
        }

        #endregion

        #region INMEASource Members

        public event NMEAForceStop OnNMEAForceStop;

        #endregion
    }

    #endregion
}
